/*----------------------------------------------------------------------------
 * Copyright (c) <2013-2017>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/

#include "lwip/opt.h"

#if BRIDGE_SUPPORT /* don't build if not configured for use in lwipopts.h */

#include "netif/bridge.h"
#include "netif/proxy_arp.h"

#define ETH_SENDER_IP_ADDR_LEN       4  /* length of an Ethernet send ip address */
#define ETH_TARGET_IP_ADDR_LEN       4  /* length of an Ethernet target ip address */
#define ETHER_ADDR_LEN  6   /* length of an Ethernet address */

struct network_db_entry arp_db[MAX_DB_NUM];

extern unsigned int g_ageing_time;
extern struct network_db_entry *db_get(struct network_db_entry *network_db);

static void db_arp_insert(struct lwip_bridge *priv,
        unsigned char *macAddr, unsigned char *networkAddr)
{
    struct network_db_entry *db = NULL;
    int hash;

    hash = bridge_hash(networkAddr, MAX_NETWORK_ADDR_LEN);
    db = priv->arphash[hash];
    while (db != NULL)
    {
        if(!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN))
        {
            (void)memcpy_s(db->macAddr, sizeof(db->macAddr), macAddr, NETIF_MAX_HWADDR_LEN);
            return;
        }

        db = db->next_hash;
    }

    db = db_get(arp_db);

    if(db == NULL) {
        PRINT_ERR("run out  nat25  table!\n");
        return;
    }

    (void)memcpy_s(db->networkAddr, sizeof(db->networkAddr), networkAddr, MAX_NETWORK_ADDR_LEN);
    (void)memcpy_s(db->macAddr, sizeof(db->macAddr), macAddr, NETIF_MAX_HWADDR_LEN);
    (void)atomic_set(&db->use_count, 1); /*lint !e64*/

    bridge_hash_link(priv->arphash, db, hash);
}

static struct network_db_entry *db_arp_lookup(struct lwip_bridge *priv,  unsigned char *networkAddr,
    unsigned char *mac)
{
    struct network_db_entry *db = NULL;

    db = priv->arphash[bridge_hash(networkAddr, MAX_NETWORK_ADDR_LEN)];
    while (db != NULL)
    {
        if ((!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN)) &&
            (!memcmp(db->macAddr, mac, NETIF_MAX_HWADDR_LEN)))
        {
            return db;
        }

        db = db->next_hash;
    }

    return NULL;
}

int proxy_arp_add(struct proxy_arp_info *arp_info)
{
    unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];

    if (g_lwip_bridge.init_flag != TRUE)
    {
        PRINT_ERR("Bridge has not been initialized!\n");
        return -1;
    }

    if (NULL == arp_info)
    {
        PRINT_ERR("%s, The parameter is NULL\n", __FUNCTION__);
        return -1;
    }

    generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, arp_info->ipAddr);
    db_arp_insert(&g_lwip_bridge, arp_info->macAddr, networkAddr);

    return 0;
}

int proxy_arp_delete(struct proxy_arp_info *arp_info)
{
    struct network_db_entry *db = NULL;
    unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];

    if (g_lwip_bridge.init_flag != TRUE)
    {
        PRINT_ERR("Bridge has not been initialized!\n");
        return -1;
    }

    if (NULL == arp_info)
    {
        PRINT_ERR("%s, The parameter is NULL\n", __FUNCTION__);
        return -1;
    }

    generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, arp_info->ipAddr);
    db = db_arp_lookup(&g_lwip_bridge, networkAddr, arp_info->macAddr);
    if (NULL == db)
    {
        PRINT_ERR("Do not find the db!\n");
        return -1;
    }

    bridge_hash_unlink(db);
    db->used = 0;

    return 0;
}

static u32_t  proxy_get_mac_by_ipv4(struct lwip_bridge *priv, u8_t *ipv4, u8_t *mac)
{
    unsigned char networkAddr[MAX_NETWORK_ADDR_LEN];
    struct network_db_entry *db = NULL;

    generate_ipv4_network_addr(networkAddr, MAX_NETWORK_ADDR_LEN, ipv4);

    db = priv->arphash[bridge_hash(networkAddr, MAX_NETWORK_ADDR_LEN)];
    while (db != NULL)
    {
        if(!memcmp(db->networkAddr, networkAddr, MAX_NETWORK_ADDR_LEN))
        {
            (void)memcpy_s(mac, ETHER_ADDR_LEN, db->macAddr, ETHER_ADDR_LEN);

            return 1;
        }

        db = db->next_hash;
    }

    return 0;
}

static  inline s32_t  eth_header(struct eth_hdr *ethhdr, u16_t type,
           void *daddr, void *saddr, u32_t len)
{
    if (type != 0x0001 && type != 0x0004)
    {
        ethhdr->type = htons(type); /*lint !e516*/
    }
    else
    {
        ethhdr->type = htons(len); /*lint !e516*/
    }

    (void)memcpy_s(ethhdr->src.addr, sizeof(ethhdr->src.addr), saddr, 6);

    if (daddr != NULL)
    {
        (void)memcpy_s(ethhdr->dest.addr, sizeof(ethhdr->dest.addr), daddr, 6);
        return 14;
    }

    return -14;
}

struct pbuf *proxy_arp_create(u32_t type, u32_t ptype, u32_t dest_ip,
                                                    struct pbuf *p, u32_t src_ip,
                                                   u8_t *dest_hw,
                                                   u8_t *src_hw,
                                                   u8_t *target_hw)
{
    struct pbuf *pst_pbuf = NULL;
    struct eth_arphdr *pst_arp = NULL;
    struct eth_hdr *ethhdr = NULL;
    u8_t   *pst_arp_ptr = NULL;

    pst_pbuf = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
    if (pst_pbuf == NULL)
    {
        return NULL;
    }

    ethhdr = (struct eth_hdr *)pst_pbuf->payload;
    pst_arp = (struct eth_arphdr *)((u8_t *)pst_pbuf->payload + sizeof(struct eth_hdr));
    ethhdr->type = htons(ETHER_TYPE_ARP);

    /*
     *  Fill the device header for the ARP frame
     */
    if (eth_header(ethhdr, ptype, dest_hw, src_hw, 14) < 0)
    {
        (void)pbuf_free(pst_pbuf);
        return NULL;
    }

    pst_arp->us_ar_hrd = htons(1);
    pst_arp->us_ar_pro = htons(ETHER_TYPE_IP);
    pst_arp->uc_ar_hln = 6;
    pst_arp->uc_ar_pln = 4;
    pst_arp->us_ar_op = htons(type); /*lint !e516*/

    pst_arp_ptr = (u8_t *)pst_arp + 8 ;

    (void)memcpy_s(pst_arp_ptr, 6, src_hw, 6);
    pst_arp_ptr += 6;
    (void)memcpy_s(pst_arp_ptr, 4, (u8_t *)&src_ip, 4);
    pst_arp_ptr += 4;
    if (target_hw != NULL)
    {
        (void)memcpy_s(pst_arp_ptr, 6, target_hw, 6);
    }
    else
    {
        (void)memset_s(pst_arp_ptr, ETHER_ADDR_LEN, 0, 6);
    }

    pst_arp_ptr += 6;
    (void)memcpy_s(pst_arp_ptr, 4, (u8_t *)&dest_ip, 4);

    return pst_pbuf;

}

struct pbuf *proxy_arp_rcv_req(struct lwip_bridge *priv, struct pbuf *p, struct eth_arphdr *arp_hdr)
{
    struct pbuf *pst_pbuf = NULL;
    u8_t    src_mac[ETHER_ADDR_LEN];
    u8_t    tgt_mac[ETHER_ADDR_LEN];
    u32_t   src_ipv4 = 0;
    u32_t   tgt_ipv4 = 0;
    u32_t   ret = 0;

    (void)memcpy_s(src_mac, sizeof(src_mac), arp_hdr->auc_ar_sha, ETHER_ADDR_LEN);
    (void)memcpy_s(&src_ipv4, ETH_SENDER_IP_ADDR_LEN, arp_hdr->auc_ar_sip, ETH_SENDER_IP_ADDR_LEN);
    (void)memcpy_s(&tgt_ipv4, ETH_TARGET_IP_ADDR_LEN, arp_hdr->auc_ar_tip, ETH_TARGET_IP_ADDR_LEN);

    ret = proxy_get_mac_by_ipv4(priv, (u8_t *)&tgt_ipv4, tgt_mac);
    if (ret == 0) {
        return NULL;
    }

    pst_pbuf = proxy_arp_create(ARP_REPLY, ETHER_TYPE_ARP, src_ipv4, p, tgt_ipv4,
                                         src_mac, tgt_mac, src_mac);
    if (pst_pbuf == NULL)
    {
        return NULL;
    }

    return pst_pbuf;
}

#endif
